/*
 * 
 * ....................................................................
 * ...######..#####.....##....####..#####..####..#####..######....##...
 * .....##....##..#....###...##.....##.....##....##..#....##.....###...
 * .....##....####....#.##....##....####...####..####.....##....#.##...
 * .....##....##.#...#####.....##...##.....##....##.#.....##...#####...
 * .....##....##..#.#...##..####....##.....####..##..#....##..#...##...
 * ....................................................................
 */

package ua.trasferta.common.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

import javax.activation.MimetypesFileTypeMap;

import ua.trasferta.common.annotations.createBy;
import static ua.trasferta.common.team.TeamMember.*;

/**
 * If you want to read a file<br>
 * Stay and wait at least a while<br>
 * Read the rules because if not<br>
 * An exception could be got<br>
 * utf ENCODING 8<br>
 * Should be used, because it's great!<br>
 * Something else? Well, just don't try<br>
 * Hardly can explain you why<br>
 * But if this rule is forgotten<br>
 * hieriglyph could be gotten<br>
 * Have the line o separate?<br>
 * Go ahead, don't be afraid<br>
 * <br>
 * <br>
 * Press the enter on your own<br>
 * And the character well known<br>
 * Would be recognized correctly<br>
 * Quickly, easily, exactly<br>
 * <br>
 * If you read this till the end<br>
 * you' ll get the right content<br>
 * 
 * @author Anton Podvaiznikov
 */
@createBy(authors = {ANTON_PODVIAZNIKOV, KATERYNA_TULUZOVA})
public class Files
{
	/**
	 * Reads text file specified by the <i>file</i> parameter, assuming that
	 * file is UTF-8 character encoding compatible. Line separator character is
	 * respected.
	 * 
	 * @param file - file, from which text data should be read.
	 * @return string with contents of the file.
	 * @throws FileNotFoundException - if no such file found.
	 * @throws IOException - if an I/O error occurs.
	 */
	public static String readFile(File file) throws FileNotFoundException, IOException
	{
		return Files.readStream(new FileInputStream(file));
	}

	/**
	 * <p>
	 * Reads bytes and decodes them into characters using a UTF-8 encoding.
	 * Result content of stream returns as string. Line separator character is
	 * respected.
	 * </p>
	 * 
	 * @param stream Stream of bytes to be read.
	 * @throws IOException If an I/O error occurs.
	 * @return <code>{@link String}</code> Content of input stream.
	 */
	public static String readStream(InputStream stream) throws IOException
	{
		BufferedReader reader = null;
		StringBuilder stringBuilder = new StringBuilder();
		String result = null;
		try
		{
			reader = new BufferedReader(new InputStreamReader(stream, Charset.UTF8.getCharSet()));
			char[] buffer = new char[4096];
			int length = 0;
			while ((length = reader.read(buffer)) != -1)
			{
				stringBuilder.append(buffer, 0, length);
			}
			result = stringBuilder.toString();
		}
		catch (UnsupportedEncodingException e)
		{
			String msg =
					"Unsupported encoding exception occured while reading stream '" + stream.toString()
							+ "' using encoding " + Charset.UTF8.getCharSet() + ".";
			throw new IOException(msg);
		}
		finally
		{
			if (reader != null)
			{
				reader.close();
			}
		}
		return result;
	}

	/**
	 * Writes text data specified by the <i>data</i> parameter to file specified
	 * by the <i>file</i> parameter, using UTF-8 character encoding.
	 * 
	 * @param data - string that contains text data, that should be stored to
	 *            file.
	 * @param file - file, to which text data should be stored.
	 * @throws IOException - if an I/O error occurs.
	 */
	public static void writeFile(String data, File file) throws IOException
	{
		BufferedWriter writer = null;
		try
		{
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), Charset.UTF8.getCharSet()));
			writer.write(data);
		}
		finally
		{
			if (writer != null)
			{
				writer.flush();
				writer.close();
			}
		}
	}

	/**
	 * Reads all available from the specified {@link InputStream} object and
	 * returns it as array of bytes.
	 * 
	 * @param inputStream input stream that should be read.
	 * @return data as array of bytes retrieved from the given
	 *         {@link InputStream} object.
	 * @throws IOException if an I/O error occures while reading.
	 */
	public static byte[] getData(InputStream inputStream) throws IOException
	{
		Objects.checkNotNull(inputStream);
		try
		{
			byte[] data = new byte[inputStream.available()];
			int length = inputStream.read(data);
			if (length < data.length)
			{
				throw new IOException("Only " + length + " bytes read from input stream actually. Expected " + data.length
						+ " bytes.");
			}
			return data;
		}
		finally
		{
			Files.cleanup(inputStream);
		}
	}

	/**
	 * Writes inputstream to given file.
	 * 
	 * @param is
	 * @param file
	 * @throws IOException
	 */
	public static void writeContentToFile(InputStream is, File file) throws IOException
	{
		OutputStream out = new FileOutputStream(file);
		try
		{
			byte buf[] = new byte[1024];
			int len;
			while ((len = is.read(buf)) > 0)
			{
				out.write(buf, 0, len);
			}
			out.close();
			is.close();
		}
		finally
		{
			Files.cleanup(is, out);
		}
	}

	/**
	 * Closes {@link Closeable} instance.
	 * 
	 * @param closeables - an array of the {@link Closeable}s.
	 * @throws IOException - if it's impossible to close.
	 */
	public static void cleanup(Closeable... closeables) throws IOException
	{
		for (Closeable closeable : closeables)
		{
			Files.cleanup(closeable);
		}
	}

	/**
	 * Closes inputstream.
	 * 
	 * @param in
	 * @throws IOException
	 */
	public static void cleanup(Closeable closeable) throws IOException
	{
		if (closeable != null)
		{
			closeable.close();
		}
	}

	/**
	 * Gets mime type for the local file.
	 * 
	 * @param file
	 * @return mime type of the local file.
	 */
	public static String getMimeType(String fileName)
	{
		return new MimetypesFileTypeMap().getContentType(fileName);
	}
}